grammar gramatica;

@members{
HashMap variables = new HashMap();
HashMap unidades = new HashMap();

}
@header {
import java.util.HashMap;
}


fragment
CHAR:  'a'..'z' | 'A'..'Z'
    ;
    
fragment
DIGITO0A9
	:	 '0'..'9'
	;

fragment
EXPONENT : ('e'|'E') ('+'|'-')? DIGITO0A9+ ;

fragment
HEX_DIGIT : (DIGITO0A9|'a'..'f'|'A'..'F') ;


fragment
OCTAL_ESC
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;

fragment
UNICODE_ESC
    :   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
    ;


// TOKENS DE LA GRAMATICA

VAR 	:	'var'
	;
	
UNIDADES:	'unidades'
	;

ID 	:	CHAR (CHAR | DIGITO0A9)*
	;
	
MAS	:	'+'
	;

MENOS	:	'-'
	;

POR	:	'*'
	;

DIV	:	'/'
	;

LB	:	'('
	;

RB	:	')'
	;

COMA	:	','
	;

DOTCOM	:	';'
	;

POT 	:	'^'
	;

IGUAL	:	'='
	;	
	
NUMERO 	:	 DIGITO0A9+ ('.' DIGITO0A9+)?

	;

SKIP  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) 
    ;

//------------------------------- PRODUCCIONES----------------------------------------------

programa
	: 
	(sentencia SKIP? DOTCOM SKIP?)*
	;

sentencia 
	:
	UNIDADES SKIP identificadores | VAR SKIP ID SKIP? IGUAL SKIP? {
	if(variables.constainsKeys($ID.text))
	{
		System.out.println("la variable ya esta definida"); 
	}else{
	variables.put($ID.text, 1);}
	} expr | expr
	// ID de la variable aun no declarado antes como unidad
	// ID's de las unidades no declarados antes como variables
	;

identificadores 
	:
	{int aux1, aux2}
	ID {aux1=$ID.text; if(variables.constainsKeys(aux1))
	{
		System.out.println("la unidad ya esta definida"); 
	}else{
		unidades.put(aux1, 1);}
	} (COMA SKIP? ID 	
	{aux2=$ID.text; if(variables.constainsKeys(aux2))
	{
		System.out.println("la unidad ya esta definida"); 
	}else{
	variables.put(aux2, 1);}
	})*
	;

expr 	returns [int valor=0, string unidad=" "; int exponente=0;]
	:	
	{int a2, exp; string op, u2;}
	e1 {valor=e1.valor; unidad=e1.unidad; exponente=e1.exponente;} (SKIP (MAS {op="MAS"} |MENOS {op="MENOS"}) SKIP e2 {a2=e2.valor;
	u2 = e2.unidad; exp = e2.exponente;
	if(a1.unidad == a2.unidad && exponente == exp)
	{
		if(op=="MAS")
		{
			valor=valor+a2;
		}else{
			valor=valor-a2;
		}
	}else{
		throw new CalculadoraException("No se puede sumar/restar unidades diferentes o iguales unidades con diferentes exponentes");
	}
	} )*;
		
e1    	returns [int valor=0, string unidad=" "; int exponente=0;]:
	{int a2, exp1, exp2, val_aux; string u1, u2, op; }
	
	e2 {valor=e2.valor; unidad=e2.unidad, exponente=e2.exponente} (( (SKIP? POR SKIP?) => SKIP? POR SKIP?| (SKIP? DIV {op="DIV"} SKIP?) => SKIP? DIV SKIP? | SKIP) e2 {a2 = e2.valor; 
	u2=e2.unidad; exp2=e2.exponente;
	if(u1==u2)
	{
		if(op=="DIV")
		{
			exponente = exponente - exp2;
			val_aux = valor /a2;
		}else{
			exponente = exponente + exp2;
			val_aux = valor * a2;
		}
		if(exponente==0)
		{
			valor=0;
		}else{
			valor = val_aux;
		}
		
	}else{
		//se conservan las unidades con sus exponente: e1.dicc = e2.dicc union e2.dicc
		if(op=="DIV")
		{
			valor = valor / a2;
		}else{
		
			valor = valor * a2;
		}	 
	}
	
	})*	
	;

e2 	returns [int valor=0; string unidad=" "; int exponente=0]
	:	
	{string op;}
	(MENOS{op="MENOS"})? e3 {if(op=="MENOS"){valor=e3.valor*(-1);}else{valor=e3.valor;} exponente=e3.exponente; unidad=e3.unidad;}
	;
	
e3 	returns [int valor=0; string unidad=" "; int exponente=0]:

	e4 {unidad = e4.unidad; exponente=e4.exponente}(POT NUMERO {valor=(e4.valor)^NUMERO;})? {valor=e4.valor;}
	;

e4	returns [int valor=0, string unidad=" "; int exponente=0]
	:
 	NUMERO {valor=NUMERO;}| ID {valor=1; unidad=ID; exponente=1;} | LB expr RB {valor=expr.valor; unidad=expr.unidad; exponente= expr.exponente;}
 	// ID ha sido declarada antes de esta llamada
	;
